Week 3 course material Liberty BASIC programming course Copyright 1996 Shoptalk Systems All Rights Reserved Reading and Writing Sequential Files ============================================================================== Now we will learn about how to work with disk files. With few exceptions, all personal computers have at least one floppy disk drive and one hard disk drive. Liberty BASIC provides ways to write information to disk files on these devices, and we can take advantage of this when we write our programs. There are two ways to read and write files in Liberty BASIC. One is called sequential and the other is called random access. We will use the sequential method for our examples here. The reason it is called sequential is that when reading or writing we start at the beginning of the file and work one item at a time to the end. An item can be words or characters separated by commas, or an item can be a complete line of data. Let's familiarize ourselves with a few Liberty BASIC statements that help us work with files. OPEN ------------------------------------------------------------------------------ The OPEN statement causes Liberty BASIC to open a file. A file must be opened if we want to write into it or read from it. There are several ways to open any file for sequential access. These 'ways' are called modes. The OUTPUT mode: The OUTPUT mode is for writing to a file. This is what an OPEN statement for OUTPUT looks like: open "myfile.txt" for output as #myHandle You can see the OUTPUT mode is specified. The last item on the line is #myHandle. It is a name (called a file handle) given to Liberty BASIC to use for the open file. Any code that writes to this file must include a reference to #myHandle. This is so that Liberty BASIC knows which file to write to. A file handle starts with a # character followed by any word or sequence of characters (using letters and digits). It is best to choose handles that make it easy for you to remember which file you are working with while writing your program. Some examples of valid file handles are: #1 #abc #dataFile #customers You cannot have more than one file open at a time that uses the same file handle or your program will terminate with an error. The INPUT mode: There is also a mode for reading sequentially from a file. This mode is called INPUT. Here is an example of an OPEN statement for INPUT: open "myfile.txt" for input as #myHandle CLOSE ------------------------------------------------------------------------------ The CLOSE statement is used for closing open files when we are done reading or writing them. This is a required operation when working with files and it is a very important thing to remember when writing programs. Here is how OPEN and CLOSE work together: open "myfile.txt" for output as #myHandle 'put some code in here that writes to #myHandle close #myHandle Another thing to remember is that a file opened for one mode must first be closed before it can be opened for a different mode. If you are going to read from a file you've just written to, you must close the file and reopen it, like so: open "myfile.txt" for output as #myHandle 'put some code in here that writes to #myHandle close #myHandle open "myfile.txt" for input as #myHandle 'put some code in here that reads from #myHandle close #myHandle PRINT ------------------------------------------------------------------------------ We've already seen how the PRINT statement can display text into a window on the screen. PRINT can also be used to write into a file opened for sequential OUTPUT. Here is an example: open "myfile.txt" for output as #myHandle print #myHandle, "Hello" print #myHandle, "World!" close #myHandle This little program produces a file containing two lines of text (each could be considered an item, see above). Type the code in and run it. When the program finishes executing, open Windows Notepad on MYFILE.TXT to see the result! Let's Take It For A Spin ============================================================================== Now we'll modify the AGES.BAS program from out WK2SOL.TXT file so that it saves the names and ages that we enter into a file. Take a look at this modified program: 'AGES.BAS 'Accept some names and ages from the user, then total and average them dim numbers(20) dim names$(20) print "AGES.BAS" print 'loop up to 20 times, getting numbers print "Enter up to 20 non-zero values." print "A zero or blank entry ends the series." [entryLoop] 'loop around until a zero entry or until index = 20 'get the user's name and age print "Entry "; index + 1; input name$ if name$ = "" then [endSeries] 'quit if name$ is blank print "Age "; input age index = index + 1 'add one to index names$(index) = name$ 'set the specified array item to be name$ numbers(index) = age 'set the specified array item to be age total = total + age 'add entry to the total if index = 20 then [endSeries] 'if 20 values were entered, exit loop goto [entryLoop] 'go back and get another entry [endSeries] 'entries are finished 'Set entryCount to index entryCount = index if entryCount = 0 then print "No Entries." : goto [quit] print "Entries completed." print print "Here are the "; entryCount; " entries:" print "-----------------------------" 'This loop displays each entered value in turn. 'Notice that we re-use the index variable. It 'can be confusing to use a new variable for each 'new loop. for index = 1 to entryCount print "Entry "; index; " is "; names$(index); ", age "; numbers(index) next index '*** New code starts here *** 'Write the data into ages.dat open "ages.dat" for output as #ages for index = 1 to entryCount print #ages, names$(index) print #ages, numbers(index) next index close #ages '*** New code ends here *** 'Now display the total and average value print print "The total age is "; total print "The average age is "; total / entryCount [quit] end Type the new program lines in. Run the program and enter a few names and ages. When the program finishes executing, open the file with Notepad and you'll the data you entered. It should look something like: Tom Jones 52 Victor Krueger 39 Sue White 64 Let's see how our newly added code works. 1) First we open the file AGES.DAT with the OPEN statement. It is opened for OUTPUT (writing) and its file handle is #ages. open "ages.dat" for output as #ages 2) This sets up a FOR/NEXT loop. for index = 1 to entryCount 3) Now we print a name and age, each on a separate line. print #ages, names$(index) print #ages, numbers(index) 4) Here's the back end of our FOR/NEXT loop. Loop back until index equals entryCount. 5) Now we will close AGES.DAT. close #ages Reading from a file ------------------------------------------------------------------------------ Now that we've written information to a disk file, we are going to read that information back into our program. This is done using the INPUT statement. Just as we saw PRINT used to display information in a window and to write information to a disk file, INPUT can be used to get keyboard input, or to get information from a disk file. To read from a file, it must be opened using the INPUT mode. The OPEN statement is used like so: open "ages.dat" for input as #ages Our INPUT statement for reading from a file looks a lot like the PRINT statement above: input #ages, var$ Notice we use the file handle, and then we specify a variable name to read into. In a program that reads a list of items, an INPUT statement like the one above would be placed inside of a loop. In this way each item in the file can be read in turn and stored in an array. An important point is that that we don't always know how many items have been written to AGES.DAT. This means we don't know when to stop looping around and reading items from the file. One solution is to add a PRINT statement to the program that creates the file. This PRINT statement would write the number of items at the start of the file, like so: 'Write the data into ages.dat open "ages.dat" for output as #ages print #ages, entryCount for index = 1 to entryCount print #ages, names$(index) print #ages, numbers(index) next index close #ages Then all we would need to do is read that number first, and then loop that many times to read each name and age. I will tackle it from a different direction though, because I want to introduce the EOF() function. The EOF() function stands for End Of File. For a given file handle, it will return 0 if we are not at the end of file, and -1 if we are at the end of file. For example: open "ages.dat" for input as #ages if eof(#ages) = 0 then print "NOT AT END OF FILE" close #ages The above code would print NOT AT END OF FILE because we haven't read all the way to the end of the file. Here is a version of AGES.BAS that reads it's information from the file we created above and uses EOF() to check for the end of file. 'AGES_IN.BAS 'Read names and ages from AGES.DAT, then total and average them. 'This version doesn't write the data back out to the file. dim numbers(20) dim names$(20) print "AGES_IN.BAS" print print "Reading AGES.DAT..." 'open ages.dat open "ages.dat" for input as #ages [entryLoop] 'loop around until end of file or until index = 20 'test for the end of file if eof(#ages) = -1 then [endSeries] 'get the user's name and age input #ages, name$ input #ages, age index = index + 1 'add one to index names$(index) = name$ 'set the specified array item to be name$ numbers(index) = age 'set the specified array item to be age total = total + age 'add entry to the total if index = 20 then [endSeries] 'if 20 values were entered, exit loop goto [entryLoop] 'go back and get another entry [endSeries] 'entries are finished 'close ages.dat close #ages 'Set entryCount to index entryCount = index if entryCount = 0 then print "No Entries." : goto [quit] print "Entries completed." print print "Here are the "; entryCount; " entries:" print "-----------------------------" 'This loop displays each entered value in turn. 'Notice that we re-use the index variable. It 'can be confusing to use a new variable for each 'new loop. for index = 1 to entryCount print "Entry "; index; " is "; names$(index); ", age "; numbers(index) next index 'Now display the total and average value print print "The total age is "; total print "The average age is "; total / entryCount [quit] end Challenge Exercise ------------------------------------------------------------------------------ Create a modified ARRAYS.BAS using the code below so that it keeps its list in a disk file named ARRAYS.DAT. The program must read its list from ARRAYS.DAT before asking for additional names. When the user is done entering names, or if all the slots are filled, the list will be written to ARRAYS.DAT before the program ends. See the source code below for some clues. 'ARRAYS2.BAS 'List handling with arrays and file input/output. 'This version stores more than 10 names. dim names$(50) 'set up our array to contain 50 items 'Insert code that reads ARRAYS.DAT into the array names$. [askForName] 'ask for a name input "Please give me your name ?"; yourName$ if yourName$ = "" then print "No name entered." : goto [quit] index = 0 [insertLoop] 'check to see if index points to an unused item in the array if names$(index) = "" then names$(index) = yourName$ : goto [nameAdded] index = index + 1 'add 1 to index if index < 50 then [insertLoop] 'loop back until we have counted to 50 'There weren't any available slots, inform user print "All ten name slots already used!" goto [quit] [nameAdded] 'Notify the name add was successful print yourName$; " has been added to the list." goto [askForName] [quit] 'display all the entered names print print "Here is a list of the names entered:" print "------------------------------------" for index = 0 to 49 if names$(index) <> "" then print names$(index) next index 'Insert code that saves the names$ array to ARRAYS.DAT. end